package harvard.cscie160.hw7.project;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.UnknownHostException;
import java.rmi.server.UnicastRemoteObject;

/**
 * Developed per class requirements
 * @author M Bret Blackford
 *
 */
public class Client extends UnicastRemoteObject implements ATMListener {
	
	protected Client() throws RemoteException {
		super();
	}

	public static void main(String[] args) {
		
		System.out.println();
		System.out.println(DateUtils.now() + " in Client.main()");
		try {
			Client client = new Client();
			client.run();
		} catch (Throwable e) {
			e.printStackTrace();
		} finally {
			System.exit(0);
		}
	}

	/**
	 * main methods.  Connects with available RMI services and initiates transactions.
	 * @throws RemoteException
	 */
	public void run() throws RemoteException {
		ATM atm = null;
		try {
			String atmService = "//localhost/atmfactory";
			
			System.out.println(DateUtils.now() + " in Client.run() ");
			System.out.println(DateUtils.now() + " looking for " + atmService);
			
			ATMFactory factory = (ATMFactory) Naming.lookup(atmService);
			atm = factory.getATM();
			System.out.println(DateUtils.now() + " have an ATM");
		} catch (MalformedURLException mue) {
			System.out.println(DateUtils.now() + " URL ERROR in Client.run()");
			mue.printStackTrace();
		} catch (NotBoundException nbe) {
			System.out.println(DateUtils.now() + " binding ERROR in Client.run()");
			nbe.printStackTrace();
		} catch (UnknownHostException uhe) {
			System.out.println(DateUtils.now() + " UnknownHost ERROR in Client.run()");
			uhe.printStackTrace();
		} catch (RemoteException re) {
			System.out.println(DateUtils.now() + " RemoteException ERROR in Client.run()");
			re.printStackTrace();
		}
		
		if (atm != null) {
			try {
				System.out.println(DateUtils.now() + " adding an ATM Listener");
				atm.addListener(this);
				
				System.out.println(DateUtils.now() + " checkig account authentication");
				atm.authenticate(getAccountInfo(1, "1234"));
				atm.authenticate(getAccountInfo(2, "2345"));
				atm.authenticate(getAccountInfo(3, "3456"));

				testATM(atm);

				atm.setDefaults();
				atm.removeListener(this);

			} catch (RemoteException re) {
				if (re.detail != null && re.detail instanceof ATMException) {
					System.out.println("ATM exception communicating with the ATM:");
					System.out.println(re.detail.getLocalizedMessage());
				} else {
					System.out.println("ERROR connecting with the ATM");
					throw re;
				}
			} finally {
				//clean up
				atm.endSession();
				atm = null;
			}
		}
	}


	public void sendTransactionNotification(TransactionNotification notification)
			throws RemoteException {
		
		//System.out.println(notification.getMessage());
		System.out.println(notification);
	}

	/**
	 * Runs standard test as per requirements
	 * @param atm
	 */
	public static void testATM(ATM atm) {
		
		System.out.println();
		System.out.println(DateUtils.now() + " in testATM() ");
		
		if (atm != null) {
			printBalances(atm);
			performTestOne(atm);
			performTestTwo(atm);
			performTestThree(atm);
			performTestFour(atm);
			performTestFive(atm);
			performTestSix(atm);
			performTestSeven(atm);
			performTestEight(atm);
			performTestNine(atm);
			printBalances(atm);
		}
	}

	/**
	 * Required per class instruction
	 * @param atm
	 */
	public static void printBalances(ATM atm) {
		printTestSeperation("BALANCES");
		try {
			System.out.println("Balance(0000001): $" + atm.getBalance(getAccountInfo(0000001, "1234")));
			System.out.println("Balance(0000002): $" + atm.getBalance(getAccountInfo(0000002,"2345")));
			System.out.println("Balance(0000003): $" + atm.getBalance(getAccountInfo(0000003,"3456")));
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println();
	}

	/**
	 * This test is giving me grief. Having trouble with the exception.
	 * @param atm
	 */
	public static void performTestOne(ATM atm){
		printTestSeperation("One");
		try{
			atm.getBalance( getAccountInfo(0000001,"5555") );
		}catch(Exception e) {
			System.out.println(DateUtils.now() + "Failed as expected: " + e);
		}
	}

	public static void performTestTwo(ATM atm) {
		printTestSeperation("Two");
		try {
			atm.withdraw( getAccountInfo(0000002,"2345"), 500 );
		} catch (Exception e) {
			System.out.println("Failed as expected: " + e);
		}
	}

	public static void performTestThree(ATM atm) {
		printTestSeperation("Three");
		try {
			atm.withdraw( getAccountInfo(0000001,"1234"), 50 ) ;
		} catch (Exception e) {
			System.out.println("Failed as expected: " + e);
		}
	}

	public static void performTestFour(ATM atm) {
		printTestSeperation("Four");
		try {
			atm.deposit( getAccountInfo(0000001,"1234"), 500);
		} catch (Exception e) {
			System.out.println("Unexpected error: " + e);
		}
	}

	public static void performTestFive(ATM atm) {
		printTestSeperation("Five");
		try {
			atm.deposit( getAccountInfo(0000002,"2345"), 100 );
		} catch (Exception e) {
			System.out.println("Unexpected error: " + e);
		}
	}

	public static void performTestSix(ATM atm) {
		printTestSeperation("Six");
		try {
			atm.withdraw( getAccountInfo(0000001,"1234"), 100 );
		} catch (Exception e) {
			System.out.println("Unexpected error: " + e);
		}
	}

	public static void performTestSeven(ATM atm) {
		printTestSeperation("Seven");
		try {
			atm.withdraw( getAccountInfo(0000003,"3456"), 300 );
		} catch (Exception e) {
			System.out.println("Unexpected error: " + e);
		}
	}

	public static void performTestEight(ATM atm) {
		printTestSeperation("Eight");
		try {
			atm.withdraw( getAccountInfo(0000001,"1234"), 200 );
		} catch (Exception e) {
			System.out.println("Failed as expected: " + e);
		}
	}

	public static void performTestNine(ATM atm) {
		printTestSeperation("Nine");
		try {
			atm.transfer(getAccountInfo(0000001,"1234"), getAccountInfo(0000002,"2345"), 100);
		} catch (Exception e) {
			System.out.println("Unexpected error: " + e);
		}
	}
	
	
	/**
	 * Helper method used as per requirements
	 * @param accountID
	 * @param PIN
	 * @return
	 */
	private static AccountInfo getAccountInfo(int accountID, String PIN){
		
		Integer intPIN = new Integer(PIN);
		
		AccountInfo account = new AccountInfo();
		account.setAccountNumber(accountID);
		account.setPIN(intPIN);
		
		return account;
	}
	
	/**
	 * method to aid in output print formatting
	 * @param testNo
	 */
	private static void printTestSeperation(String testNo){
		System.out.println();
		System.out.println(DateUtils.now() + " ******** test [" + testNo + "] ********");
	}
}


